{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%%shell\n# Installs the latest dev build of TVM from PyPI. If you wish to build\n# from source, see https://tvm.apache.org/docs/install/from_source.html\npip install apache-tvm --pre"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n\n# 3. microTVM Ahead-of-Time (AOT) Compilation\n**Authors**:\n[Mehrdad Hessar](https://github.com/mehrdadh),\n[Alan MacDonald](https://github.com/alanmacd)\n\nThis tutorial is showcasing microTVM host-driven AoT compilation with\na TFLite model. AoTExecutor reduces the overhead of parsing graph at runtime\ncompared to GraphExecutor. Also, we can have better memory management using ahead\nof time compilation. This tutorial can be executed on a x86 CPU using C runtime (CRT)\nor on Zephyr platform on a microcontroller/board supported by Zephyr.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Install microTVM Python dependencies\n\nTVM does not include a package for Python serial communication, so\nwe must install one before using microTVM. We will also need TFLite\nto load models.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%%shell\npip install pyserial==3.5 tflite==2.1"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import os\n\n# By default, this tutorial runs on x86 CPU using TVM's C runtime. If you would like\n# to run on real Zephyr hardware, you must export the `TVM_MICRO_USE_HW` environment\n# variable. Otherwise (if you are using the C runtime), you can skip installing\n# Zephyr. It takes ~20 minutes to install Zephyr.\nuse_physical_hw = bool(os.getenv(\"TVM_MICRO_USE_HW\"))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Install Zephyr\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%%shell\n# Install west and ninja\npython3 -m pip install west\napt-get install -y ninja-build\n\n# Install ZephyrProject\nZEPHYR_PROJECT_PATH=\"/content/zephyrproject\"\nexport ZEPHYR_BASE=${ZEPHYR_PROJECT_PATH}/zephyr\nwest init ${ZEPHYR_PROJECT_PATH}\ncd ${ZEPHYR_BASE}\ngit checkout v3.2-branch\ncd ..\nwest update\nwest zephyr-export\nchmod -R o+w ${ZEPHYR_PROJECT_PATH}\n\n# Install Zephyr SDK\ncd /content\nZEPHYR_SDK_VERSION=\"0.15.2\"\nwget \"https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${ZEPHYR_SDK_VERSION}/zephyr-sdk-${ZEPHYR_SDK_VERSION}_linux-x86_64.tar.gz\"\ntar xvf \"zephyr-sdk-${ZEPHYR_SDK_VERSION}_linux-x86_64.tar.gz\"\nmv \"zephyr-sdk-${ZEPHYR_SDK_VERSION}\" zephyr-sdk\nrm \"zephyr-sdk-${ZEPHYR_SDK_VERSION}_linux-x86_64.tar.gz\"\n\n# Install python dependencies\npython3 -m pip install -r \"${ZEPHYR_BASE}/scripts/requirements.txt\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Import Python dependencies\n\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import numpy as np\nimport pathlib\nimport json\n\nimport tvm\nfrom tvm import relay\nimport tvm.micro.testing\nfrom tvm.relay.backend import Executor, Runtime\nfrom tvm.contrib.download import download_testdata"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Import a TFLite model\n\nTo begin with, download and import a Keyword Spotting TFLite model.\nThis model is originally from [MLPerf Tiny repository](https://github.com/mlcommons/tiny).\nTo test this model, we use samples from [KWS dataset provided by Google](https://ai.googleblog.com/2017/08/launching-speech-commands-dataset.html).\n\n**Note:** By default this tutorial runs on x86 CPU using CRT, if you would like to run on Zephyr platform\nyou need to export `TVM_MICRO_USE_HW` environment variable.\n\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "MODEL_URL = \"https://github.com/mlcommons/tiny/raw/bceb91c5ad2e2deb295547d81505721d3a87d578/benchmark/training/keyword_spotting/trained_models/kws_ref_model.tflite\"\nMODEL_PATH = download_testdata(MODEL_URL, \"kws_ref_model.tflite\", module=\"model\")\nSAMPLE_URL = \"https://github.com/tlc-pack/web-data/raw/main/testdata/microTVM/data/keyword_spotting_int8_6.pyc.npy\"\nSAMPLE_PATH = download_testdata(SAMPLE_URL, \"keyword_spotting_int8_6.pyc.npy\", module=\"data\")\n\ntflite_model_buf = open(MODEL_PATH, \"rb\").read()\ntry:\n    import tflite\n\n    tflite_model = tflite.Model.GetRootAsModel(tflite_model_buf, 0)\nexcept AttributeError:\n    import tflite.Model\n\n    tflite_model = tflite.Model.Model.GetRootAsModel(tflite_model_buf, 0)\n\ninput_shape = (1, 49, 10, 1)\nINPUT_NAME = \"input_1\"\nrelay_mod, params = relay.frontend.from_tflite(\n    tflite_model, shape_dict={INPUT_NAME: input_shape}, dtype_dict={INPUT_NAME: \"int8\"}\n)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Defining the target\n\nNow we need to define the target, runtime and executor. In this tutorial, we focused on\nusing AOT host driven executor. We use the host micro target which is for running a model\non x86 CPU using CRT runtime or running a model with Zephyr platform on qemu_x86 simulator\nboard. In the case of a physical microcontroller, we get the target model for the physical\nboard (E.g. nucleo_l4r5zi) and change `BOARD` to supported Zephyr board.\n\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Use the C runtime (crt) and enable static linking by setting system-lib to True\nRUNTIME = Runtime(\"crt\", {\"system-lib\": True})\n\n# Simulate a microcontroller on the host machine. Uses the main() from `src/runtime/crt/host/main.cc`.\n# To use physical hardware, replace \"host\" with something matching your hardware.\nTARGET = tvm.micro.testing.get_target(\"crt\")\n\n# Use the AOT executor rather than graph or vm executors. Don't use unpacked API or C calling style.\nEXECUTOR = Executor(\"aot\")\n\nif use_physical_hw:\n    BOARD = os.getenv(\"TVM_MICRO_BOARD\", default=\"nucleo_l4r5zi\")\n    SERIAL = os.getenv(\"TVM_MICRO_SERIAL\", default=None)\n    TARGET = tvm.micro.testing.get_target(\"zephyr\", BOARD)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Compile the model\n\nNow, we compile the model for the target:\n\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "with tvm.transform.PassContext(opt_level=3, config={\"tir.disable_vectorize\": True}):\n    module = tvm.relay.build(\n        relay_mod, target=TARGET, params=params, runtime=RUNTIME, executor=EXECUTOR\n    )"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Create a microTVM project\n\nNow that we have the compiled model as an IRModule, we need to create a firmware project\nto use the compiled model with microTVM. To do this, we use Project API. We have defined\nCRT and Zephyr microTVM template projects which are used for x86 CPU and Zephyr boards\nrespectively.\n\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "template_project_path = pathlib.Path(tvm.micro.get_microtvm_template_projects(\"crt\"))\nproject_options = {}  # You can use options to provide platform-specific options through TVM.\n\nif use_physical_hw:\n    template_project_path = pathlib.Path(tvm.micro.get_microtvm_template_projects(\"zephyr\"))\n    project_options = {\n        \"project_type\": \"host_driven\",\n        \"board\": BOARD,\n        \"serial_number\": SERIAL,\n        \"config_main_stack_size\": 4096,\n        \"zephyr_base\": os.getenv(\"ZEPHYR_BASE\", default=\"/content/zephyrproject/zephyr\"),\n    }\n\ntemp_dir = tvm.contrib.utils.tempdir()\ngenerated_project_dir = temp_dir / \"project\"\nproject = tvm.micro.generate_project(\n    template_project_path, module, generated_project_dir, project_options\n)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Build, flash and execute the model\nNext, we build the microTVM project and flash it. Flash step is specific to\nphysical microcontrollers and it is skipped if it is simulating a microcontroller\nvia the host main.cc or if a Zephyr emulated board is selected as the target.\nNext, we define the labels for the model output and execute the model with a\nsample with expected value of 6 (label: left).\n\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "project.build()\nproject.flash()\n\nlabels = [\n    \"_silence_\",\n    \"_unknown_\",\n    \"yes\",\n    \"no\",\n    \"up\",\n    \"down\",\n    \"left\",\n    \"right\",\n    \"on\",\n    \"off\",\n    \"stop\",\n    \"go\",\n]\nwith tvm.micro.Session(project.transport()) as session:\n    aot_executor = tvm.runtime.executor.aot_executor.AotModule(session.create_aot_executor())\n    sample = np.load(SAMPLE_PATH)\n    aot_executor.get_input(INPUT_NAME).copyfrom(sample)\n    aot_executor.run()\n    result = aot_executor.get_output(0).numpy()\n    print(f\"Label is `{labels[np.argmax(result)]}` with index `{np.argmax(result)}`\")"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.8.16"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}